package com.ktjiaoyu.travelaiagent.tools;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.system.SystemUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.*;
import java.util.regex.Pattern;

@Component
@Slf4j
public class TerminalExecuteTool {

    // 文件根目录
    @Value("${travel.ai.files.root}")
    private String fileRoot;

    // 危险命令关键词（以这些词开头的命令都被认为是危险的）
    private static final Set<String> DANGEROUS_COMMAND_PREFIXES = new HashSet<>(Arrays.asList(
        // 文件删除相关
        "rm", "del", "erase", "rd", "rmdir",
        // 磁盘格式化相关
        "format", "mkfs", "fdisk",
        // 系统关机/重启相关
        "shutdown", "halt", "reboot", "poweroff", "restart",
        // 进程终止相关
        "kill", "pkill", "killall",
        // 用户账户相关
        "userdel", "useradd", "usermod", "passwd",
        // 权限修改相关（危险的权限修改）
        "chmod", "chown", "chattr",
        // 网络配置相关
        "ifconfig", "ip addr", "route", "iptables",
        // 包管理相关（可能卸载关键包）
        "apt-get remove", "yum remove", "pacman -R",
        // 服务管理相关
        "systemctl", "service",
        // 内核相关
        "modprobe", "insmod", "rmmod"
    ));

    /**
     * 获取操作系统信息
     * 
     * @param toolContext 工具上下文
     * @return 操作系统类型（Windows或Linux）
     */
    @Tool(description = "Get operating system type, returns Windows or Linux")
    public String getOSInfo(ToolContext toolContext) {
        log.info("AI调用getOSInfo工具获取操作系统信息");
        try {
            String osName = SystemUtil.getOsInfo().getName().toLowerCase();
            if (osName.contains("windows")) {
                return "Windows";
            } else if (osName.contains("linux") || osName.contains("unix")) {
                return "Linux";
            } else {
                return "Unknown OS: " + osName;
            }
        } catch (Exception e) {
            log.error("获取操作系统信息时出现异常: {}", e.getMessage());
            return "获取操作系统信息失败: " + e.getMessage();
        }
    }

    /**
     * 执行终端命令
     * 
     * @param command 要执行的命令
     * @param timeout 超时时间（秒），默认30秒
     * @param toolContext 工具上下文
     * @return 命令执行结果
     * 
     * 调用示例：
     * 1. 先调用getOSInfo获取系统类型
     * 2. 根据系统类型决定执行的命令：
     *    - Windows系统：executeCommand("dir", 30)
     *    - Linux系统：executeCommand("ls -l", 30)
     */
    @Tool(description = "Execute terminal command. You need to call getOSInfo first to get the system type and then decide which command to execute. " +
            "Usage example: " +
            "For Windows system, execute executeCommand(\"dir\", 30); " +
            "for Linux system, execute executeCommand(\"ls -l\", 30)")
    public String executeCommand(
            @ToolParam(description = "Command to execute, must ensure the command is safe", required = true) String command,
            @ToolParam(description = "Command timeout in seconds, default 30 seconds") int timeout,
            ToolContext toolContext) {
        if(timeout <= 0) {
            timeout = 30;
        }
        // 建议用会话ID隔离操作，避免影响其他用户的会话
        String chatId = (String) toolContext.getContext().get("chatId");
        log.info("AI调用executeCommand工具执行'{}'命令，chatId: {}", command, chatId);
        
        // 检查命令是否安全
        if (!isCommandSafe(command)) {
            String errorMsg = "命令 '" + command + "' 被阻止，因为它包含危险操作，可能存在安全风险";
            log.warn(errorMsg);
            return errorMsg;
        }
        
        try {
            // 根据操作系统类型添加相应的执行前缀
            String[] cmdArray = buildCommandArray(command);
            
            // 使用Future和ExecutorService实现超时控制
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Future<String> future = executor.submit(() -> RuntimeUtil.execForStr(cmdArray));
            
            try {
                String result = future.get(timeout, TimeUnit.SECONDS);
                log.info("AI调用executeCommand工具执行'{}'命令成功", command);
                return result;
            } catch (TimeoutException e) {
                future.cancel(true);
                String errorMsg = "命令执行超时（超过" + timeout + "秒）";
                log.error(errorMsg);
                return errorMsg;
            } finally {
                executor.shutdown();
            }
        } catch (Exception e) {
            log.error("AI调用executeCommand工具执行'{}'命令出现异常: {}", command, e.getMessage());
            return "命令执行出现异常: " + e.getMessage();
        }
    }

    /**
     * 在工作路径下执行文件操作命令
     * 
     * @param command 文件操作命令（如：ls -l）
     * @param files 操作的文件列表（可为空，为空表示直接对工作路径执行命令）
     * @param timeout 超时时间（秒），默认30秒
     * @param toolContext 工具上下文
     * @return 命令执行结果
     * 
     * 调用示例：
     * 1. 先调用getOSInfo获取系统类型
     * 2. 根据系统类型决定执行的命令：
     *    - Windows系统：executeOnWorkPath("dir", null, 30)
     *    - Linux系统：executeOnWorkPath("ls -l", null, 30)
     *    - 操作特定文件：executeOnWorkPath("cat", "test.txt", 30)
     */
    @Tool(description = "Execute file operation command in working path. You need to call getOSInfo first to get the system type and then decide which command to execute. " +
            "Usage example: " +
            "For Windows system, execute executeOnWorkPath(\"dir\", null, 30); " +
            "for Linux system, execute executeOnWorkPath(\"ls -l\", null, 30)")
    public String executeOnWorkPath(
            @ToolParam(description = "File operation command (e.g., ls -l)", required = true) String command,
            @ToolParam(description = "List of files to operate on (can be null, null means execute command directly on working path)") String files,
            @ToolParam(description = "Command timeout in seconds, default 30 seconds") int timeout,
            ToolContext toolContext) {
        if(timeout <= 0) {
            timeout = 30;
        }
        // 建议用会话ID隔离操作，避免影响其他用户的会话
        String chatId = (String) toolContext.getContext().get("chatId");
        log.info("AI调用executeOnWorkPath工具执行'{}'命令，操作文件：{}，chatId: {}", command, files, chatId);
        
        // 检查命令是否安全
        if (!isCommandSafe(command)) {
            String errorMsg = "命令 '" + command + "' 被阻止，因为它包含危险操作，可能存在安全风险";
            log.warn(errorMsg);
            return errorMsg;
        }
        
        try {
            // 构建完整的工作路径
            String workPath = buildWorkPath(chatId);
            
            // 确保工作路径存在
            FileUtil.mkdir(workPath);
            
            // 构建完整的命令
            String fullCommand = command;
            if (files != null && !files.trim().isEmpty()) {
                fullCommand += " " + files;
            }
            
            // 切换到工作目录并执行命令
            String[] cmdArray = buildCommandArrayWithWorkDir(fullCommand, workPath);
            
            // 使用Future和ExecutorService实现超时控制
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Future<String> future = executor.submit(() -> RuntimeUtil.execForStr(cmdArray));
            
            try {
                String result = future.get(timeout, TimeUnit.SECONDS);
                log.info("AI调用executeOnWorkPath工具执行'{}'命令成功", command);
                return result;
            } catch (TimeoutException e) {
                future.cancel(true);
                String errorMsg = "命令执行超时（超过" + timeout + "秒）";
                log.error(errorMsg);
                return errorMsg;
            } finally {
                executor.shutdown();
            }
        } catch (Exception e) {
            log.error("AI调用executeOnWorkPath工具执行'{}'命令出现异常: {}", command, e.getMessage());
            return "命令执行出现异常: " + e.getMessage();
        }
    }

    /**
     * 检查命令是否安全（不包含危险关键词）
     * 
     * @param command 要检查的命令
     * @return 如果命令安全返回true，否则返回false
     */
    private boolean isCommandSafe(String command) {
        if (command == null || command.trim().isEmpty()) {
            return false;
        }
        
        // 转换为小写进行比较，并去除首尾空格
        String lowerCommand = command.toLowerCase().trim();
        
        // 移除命令中的引号和多余空格，规范化命令格式
        lowerCommand = lowerCommand.replaceAll("[\"']", "").replaceAll("\\s+", " ");
        
        // 检查是否以危险命令开头
        for (String dangerousPrefix : DANGEROUS_COMMAND_PREFIXES) {
            // 检查命令是否以危险前缀开头（考虑命令参数分隔）
            if (lowerCommand.startsWith(dangerousPrefix + " ") || 
                lowerCommand.equals(dangerousPrefix) ||
                // 特殊处理Windows命令的选项格式
                lowerCommand.startsWith(dangerousPrefix + "/") ||
                lowerCommand.startsWith(dangerousPrefix + "-")) {
                return false;
            }
        }
        
        // 检查一些特殊的危险命令模式
        if (lowerCommand.contains(" > /dev/sda") || 
            lowerCommand.contains(" > /dev/null") ||
            lowerCommand.contains(" :(){") ||
            Pattern.matches(".*[&|;]\\s*[&|;].*", lowerCommand)) {
            return false;
        }
        
        return true;
    }

    /**
     * 根据操作系统类型构建命令数组
     * 
     * @param command 要执行的命令
     * @return 包含执行前缀的命令数组
     */
    private String[] buildCommandArray(String command) {
        String osName = SystemUtil.getOsInfo().getName().toLowerCase();
        if (osName.contains("windows")) {
            // Windows系统使用cmd /c执行命令
            return new String[]{"cmd", "/c", command};
        } else {
            // Linux/Unix系统使用bash -c执行命令
            return new String[]{"bash", "-c", command};
        }
    }

    /**
     * 构建包含工作目录的命令数组
     * 
     * @param command 要执行的命令
     * @param workDir 工作目录
     * @return 包含执行前缀和工作目录的命令数组
     */
    private String[] buildCommandArrayWithWorkDir(String command, String workDir) {
        String osName = SystemUtil.getOsInfo().getName().toLowerCase();
        if (osName.contains("windows")) {
            // Windows系统使用cmd /c执行命令，并切换到工作目录
            return new String[]{"cmd", "/c", "cd /d \"" + workDir + "\" && " + command};
        } else {
            // Linux/Unix系统使用bash -c执行命令，并切换到工作目录
            return new String[]{"bash", "-c", "cd \"" + workDir + "\" && " + command};
        }
    }

    /**
     * 构建工作路径
     * 
     * @param chatId 会话ID
     * @return 完整的工作路径
     */
    private String buildWorkPath(String chatId) {
        if (chatId == null) {
            chatId = "";
        } else {
            chatId = chatId + File.separator; // 使用系统分隔符
        }
        // 使用系统分隔符拼接完整路径
        return this.fileRoot + File.separator + chatId;
    }
}